home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2000 March / maximum-cd-2000-03.iso / Quake3 Game Source / Q3AGameSource.exe / Main / g_team.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-18  |  21.3 KB  |  831 lines

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3.  
  4. #include <limits.h>
  5.  
  6. #include "g_local.h"
  7.  
  8. typedef enum _flag_status {
  9.     FLAG_ATBASE = 0,
  10.     FLAG_TAKEN,
  11.     FLAG_DROPPED
  12. } flagStatus_t;
  13.  
  14. typedef struct teamgame_s
  15. {
  16.     float last_flag_capture;
  17.     int last_capture_team;
  18.     flagStatus_t redStatus;
  19.     flagStatus_t blueStatus;
  20. } teamgame_t;
  21.  
  22. teamgame_t teamgame;
  23.  
  24. void Team_SetFlagStatus( int team, flagStatus_t status );
  25.  
  26. void Team_InitGame(void)
  27. {
  28.     memset(&teamgame, 0, sizeof teamgame);
  29.     teamgame.redStatus = teamgame.blueStatus = -1; // Invalid to force update
  30.  
  31.     Team_SetFlagStatus( TEAM_RED, FLAG_ATBASE );
  32.     Team_SetFlagStatus( TEAM_BLUE, FLAG_ATBASE );
  33. }
  34.  
  35. int OtherTeam(int team) {
  36.     if (team==TEAM_RED)
  37.         return TEAM_BLUE;
  38.     else if (team==TEAM_BLUE)
  39.         return TEAM_RED;
  40.     return team;
  41. }
  42.  
  43. const char *TeamName(int team)  {
  44.     if (team==TEAM_RED)
  45.         return "RED";
  46.     else if (team==TEAM_BLUE)
  47.         return "BLUE";
  48.     else if (team==TEAM_SPECTATOR)
  49.         return "SPECTATOR";
  50.     return "FREE";
  51. }
  52.  
  53. const char *OtherTeamName(int team) {
  54.     if (team==TEAM_RED)
  55.         return "BLUE";
  56.     else if (team==TEAM_BLUE)
  57.         return "RED";
  58.     else if (team==TEAM_SPECTATOR)
  59.         return "SPECTATOR";
  60.     return "FREE";
  61. }
  62.  
  63. const char *TeamColorString(int team) {
  64.     if (team==TEAM_RED)
  65.         return S_COLOR_RED;
  66.     else if (team==TEAM_BLUE)
  67.         return S_COLOR_BLUE;
  68.     else if (team==TEAM_SPECTATOR)
  69.         return S_COLOR_YELLOW;
  70.     return S_COLOR_WHITE;
  71. }
  72.  
  73. // NULL for everyone
  74. void QDECL PrintMsg( gentity_t *ent, const char *fmt, ... ) {
  75.     char        msg[1024];
  76.     va_list        argptr;
  77.     char        *p;
  78.     
  79.     va_start (argptr,fmt);
  80.     if (vsprintf (msg, fmt, argptr) > sizeof(msg)) {
  81.         G_Error ( "PrintMsg overrun" );
  82.     }
  83.     va_end (argptr);
  84.  
  85.     // double quotes are bad
  86.     while ((p = strchr(msg, '"')) != NULL)
  87.         *p = '\'';
  88.  
  89.     trap_SendServerCommand ( ( (ent == NULL) ? -1 : ent-g_entities ), va("print \"%s\"", msg ));
  90. }
  91.  
  92. /*
  93. ==============
  94. OnSameTeam
  95. ==============
  96. */
  97. qboolean OnSameTeam( gentity_t *ent1, gentity_t *ent2 ) {
  98.     if ( !ent1->client || !ent2->client ) {
  99.         return qfalse;
  100.     }
  101.  
  102.     if ( g_gametype.integer < GT_TEAM ) {
  103.         return qfalse;
  104.     }
  105.  
  106.     if ( ent1->client->sess.sessionTeam == ent2->client->sess.sessionTeam ) {
  107.         return qtrue;
  108.     }
  109.  
  110.     return qfalse;
  111. }
  112.  
  113. void Team_SetFlagStatus( int team, flagStatus_t status )
  114. {
  115.     qboolean modified = qfalse;
  116.  
  117.     switch (team) {
  118.     case TEAM_RED :
  119.         if ( teamgame.redStatus != status ) {
  120.             teamgame.redStatus = status;
  121.             modified = qtrue;
  122.         }
  123.         break;
  124.     case TEAM_BLUE :
  125.         if ( teamgame.blueStatus != status ) {
  126.             teamgame.blueStatus = status;
  127.             modified = qtrue;
  128.         }
  129.         break;
  130.     }
  131.  
  132.     if (modified) {
  133.         char st[4];
  134.  
  135.         st[0] = '0' + (int)teamgame.redStatus;
  136.         st[1] = '0' + (int)teamgame.blueStatus;
  137.         st[2] = 0;
  138.  
  139.         trap_SetConfigstring( CS_FLAGSTATUS, st );
  140.     }
  141. }
  142.  
  143. void Team_CheckDroppedItem( gentity_t *dropped )
  144. {
  145.     if (dropped->item->giTag == PW_REDFLAG)
  146.         Team_SetFlagStatus( TEAM_RED, FLAG_DROPPED );
  147.     else if (dropped->item->giTag == PW_BLUEFLAG)
  148.         Team_SetFlagStatus( TEAM_BLUE, FLAG_DROPPED );
  149. }
  150.  
  151.  
  152. /*
  153. ================
  154. Team_FragBonuses
  155.  
  156. Calculate the bonuses for flag defense, flag carrier defense, etc.
  157. Note that bonuses are not cumlative.  You get one, they are in importance
  158. order.
  159. ================
  160. */
  161. void Team_FragBonuses(gentity_t *targ, gentity_t *inflictor, gentity_t *attacker)
  162. {
  163.     int i;
  164.     gentity_t *ent;
  165.     int flag_pw, enemy_flag_pw;
  166.     int otherteam;
  167.     gentity_t *flag, *carrier = NULL;
  168.     char *c;
  169.     vec3_t v1, v2;
  170.     int team;
  171.  
  172.     // no bonus for fragging yourself
  173.     if (!targ->client || !attacker->client || targ == attacker)
  174.         return;
  175.  
  176.     team = targ->client->sess.sessionTeam;
  177.     otherteam = OtherTeam(targ->client->sess.sessionTeam);
  178.     if (otherteam < 0)
  179.         return; // whoever died isn't on a team
  180.  
  181.     // same team, if the flag at base, check to he has the enemy flag
  182.     if (team == TEAM_RED) {
  183.         flag_pw = PW_REDFLAG;
  184.         enemy_flag_pw = PW_BLUEFLAG;
  185.     } else {
  186.         flag_pw = PW_BLUEFLAG;
  187.         enemy_flag_pw = PW_REDFLAG;
  188.     }
  189.  
  190.     // did the attacker frag the flag carrier?
  191.     if (targ->client->ps.powerups[enemy_flag_pw]) {
  192.         attacker->client->pers.teamState.lastfraggedcarrier = level.time;
  193.         AddScore(attacker, CTF_FRAG_CARRIER_BONUS);
  194.         attacker->client->pers.teamState.fragcarrier++;
  195.         PrintMsg(NULL, "%s" S_COLOR_WHITE " fragged %s's flag carrier!\n",
  196.             attacker->client->pers.netname, TeamName(team));
  197.  
  198.         // the target had the flag, clear the hurt carrier
  199.         // field on the other team
  200.         for (i = 0; i < g_maxclients.integer; i++) {
  201.             ent = g_entities + i;
  202.             if (ent->inuse && ent->client->sess.sessionTeam == otherteam)
  203.                 ent->client->pers.teamState.lasthurtcarrier = 0;
  204.         }
  205.         return;
  206.     }
  207.  
  208.     if (targ->client->pers.teamState.lasthurtcarrier &&
  209.         level.time - targ->client->pers.teamState.lasthurtcarrier < CTF_CARRIER_DANGER_PROTECT_TIMEOUT &&
  210.         !attacker->client->ps.powerups[flag_pw]) {
  211.         // attacker is on the same team as the flag carrier and
  212.         // fragged a guy who hurt our flag carrier
  213.         AddScore(attacker, CTF_CARRIER_DANGER_PROTECT_BONUS);
  214.  
  215.         attacker->client->pers.teamState.carrierdefense++;
  216.         targ->client->pers.teamState.lasthurtcarrier = 0;
  217.  
  218.         team = attacker->client->sess.sessionTeam;
  219.         PrintMsg(NULL, "%s" S_COLOR_WHITE " defends %s's flag carrier against an agressive enemy\n",
  220.             attacker->client->pers.netname, TeamName(team));
  221.         return;
  222.     }
  223.  
  224.     // flag and flag carrier area defense bonuses
  225.  
  226.     // we have to find the flag and carrier entities
  227.  
  228.     // find the flag
  229.     switch (attacker->client->sess.sessionTeam) {
  230.     case TEAM_RED:
  231.         c = "team_CTF_redflag";
  232.         break;
  233.     case TEAM_BLUE:
  234.         c = "team_CTF_blueflag";
  235.         break;        
  236.     default:
  237.         return;
  238.     }
  239.  
  240.     flag = NULL;
  241.     while ((flag = G_Find (flag, FOFS(classname), c)) != NULL) {
  242.         if (!(flag->flags & FL_DROPPED_ITEM))
  243.             break;
  244.     }
  245.  
  246.     if (!flag)
  247.         return; // can't find attacker's flag
  248.  
  249.     // find attacker's team's flag carrier
  250.     for (i = 0; i < g_maxclients.integer; i++) {
  251.         carrier = g_entities + i;
  252.         if (carrier->inuse && carrier->client->ps.powerups[flag_pw])
  253.             break;
  254.         carrier = NULL;
  255.     }
  256.  
  257.     // ok we have the attackers flag and a pointer to the carrier
  258.  
  259.     // check to see if we are defending the base's flag
  260.     VectorSubtract(targ->r.currentOrigin, flag->r.currentOrigin, v1);
  261.     VectorSubtract(attacker->r.currentOrigin, flag->r.currentOrigin, v2);
  262.  
  263.     if ( ( ( VectorLength(v1) < CTF_TARGET_PROTECT_RADIUS &&
  264.         trap_InPVS(flag->r.currentOrigin, targ->r.currentOrigin ) ) ||
  265.         ( VectorLength(v2) < CTF_TARGET_PROTECT_RADIUS &&
  266.         trap_InPVS(flag->r.currentOrigin, attacker->r.currentOrigin ) ) ) &&
  267.         attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam) {
  268.  
  269.         // we defended the base flag
  270.         AddScore(attacker, CTF_FLAG_DEFENSE_BONUS);
  271.         attacker->client->pers.teamState.basedefense++;
  272.         if (flag->r.svFlags & SVF_NOCLIENT) {
  273.             PrintMsg(NULL, "%s" S_COLOR_WHITE " defends the %s base.\n",
  274.                 attacker->client->pers.netname, 
  275.                 TeamName(attacker->client->sess.sessionTeam));
  276.         } else {
  277.             PrintMsg(NULL, "%s" S_COLOR_WHITE " defends the %s flag.\n",
  278.                 attacker->client->pers.netname, 
  279.                 TeamName(attacker->client->sess.sessionTeam));
  280.         }
  281.         return;
  282.     }
  283.  
  284.     if (carrier && carrier != attacker) {
  285.         VectorSubtract(targ->r.currentOrigin, carrier->r.currentOrigin, v1);
  286.         VectorSubtract(attacker->r.currentOrigin, carrier->r.currentOrigin, v1);
  287.  
  288.         if ( ( ( VectorLength(v1) < CTF_ATTACKER_PROTECT_RADIUS &&
  289.             trap_InPVS(carrier->r.currentOrigin, targ->r.currentOrigin ) ) ||
  290.             ( VectorLength(v2) < CTF_ATTACKER_PROTECT_RADIUS &&
  291.                 trap_InPVS(carrier->r.currentOrigin, attacker->r.currentOrigin ) ) ) &&
  292.             attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam) {
  293.             AddScore(attacker, CTF_CARRIER_PROTECT_BONUS);
  294.             attacker->client->pers.teamState.carrierdefense++;
  295.             PrintMsg(NULL, "%s" S_COLOR_WHITE " defends the %s's flag carrier.\n",
  296.                 attacker->client->pers.netname, 
  297.                 TeamName(attacker->client->sess.sessionTeam));
  298.             return;
  299.         }
  300.     }
  301. }
  302.  
  303. /*
  304. ================
  305. Team_CheckHurtCarrier
  306.  
  307. Check to see if attacker hurt the flag carrier.  Needed when handing out bonuses for assistance to flag
  308. carrier defense.
  309. ================
  310. */
  311. void Team_CheckHurtCarrier(gentity_t *targ, gentity_t *attacker)
  312. {
  313.     int flag_pw;
  314.  
  315.     if (!targ->client || !attacker->client)
  316.         return;
  317.  
  318.     if (targ->client->sess.sessionTeam == TEAM_RED)
  319.         flag_pw = PW_BLUEFLAG;
  320.     else
  321.         flag_pw = PW_REDFLAG;
  322.  
  323.     if (targ->client->ps.powerups[flag_pw] &&
  324.         targ->client->sess.sessionTeam != attacker->client->sess.sessionTeam)
  325.         attacker->client->pers.teamState.lasthurtcarrier = level.time;
  326. }
  327.  
  328.  
  329. gentity_t *Team_ResetFlag(int team)
  330. {
  331.     char *c;
  332.     gentity_t *ent, *rent = NULL;
  333.  
  334.     switch (team) {
  335.     case TEAM_RED:
  336.         c = "team_CTF_redflag";
  337.         break;
  338.     case TEAM_BLUE:
  339.         c = "team_CTF_blueflag";
  340.         break;
  341.     default:
  342.         return NULL;
  343.     }
  344.  
  345.     ent = NULL;
  346.     while ((ent = G_Find (ent, FOFS(classname), c)) != NULL) {
  347.         if (ent->flags & FL_DROPPED_ITEM)
  348.             G_FreeEntity(ent);
  349.         else {
  350.             rent = ent;
  351.             RespawnItem(ent);
  352.         }
  353.     }
  354.  
  355.     Team_SetFlagStatus( team, FLAG_ATBASE );
  356.  
  357.     return rent;
  358. }
  359.  
  360. void Team_ResetFlags(void)
  361. {
  362.     Team_ResetFlag(TEAM_RED);
  363.     Team_ResetFlag(TEAM_BLUE);
  364. }
  365.  
  366. void Team_ReturnFlagSound(gentity_t *ent, int team)
  367. {
  368.     // play powerup spawn sound to all clients
  369.     gentity_t    *te;
  370.  
  371.     if (ent == NULL) {
  372.         G_Printf ("Warning:  NULL passed to Team_ReturnFlagSound\n");
  373.         return;
  374.     }
  375.  
  376.     te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_SOUND );
  377.     te->s.eventParm = G_SoundIndex( team == TEAM_RED ?
  378.         "sound/teamplay/flagret_red.wav" :
  379.         "sound/teamplay/flagret_blu.wav" );
  380.     te->r.svFlags |= SVF_BROADCAST;
  381. }
  382.  
  383. void Team_ReturnFlag(int team)
  384. {
  385.     Team_ReturnFlagSound(Team_ResetFlag(team), team);
  386.     PrintMsg(NULL, "The %s flag has returned!\n", TeamName(team));
  387. }
  388.  
  389. void Team_FreeEntity(gentity_t *ent)
  390. {
  391.     if (ent->item->giTag == PW_REDFLAG)
  392.         Team_ReturnFlag(TEAM_RED);
  393.     else if (ent->item->giTag == PW_BLUEFLAG)
  394.         Team_ReturnFlag(TEAM_BLUE);
  395. }
  396.  
  397. /*
  398. ==============
  399. Team_DroppedFlagThink
  400.  
  401. Automatically set in Launch_Item if the item is one of the flags
  402.  
  403. Flags are unique in that if they are dropped, the base flag must be respawned when they time out
  404. ==============
  405. */
  406. void Team_DroppedFlagThink(gentity_t *ent)
  407. {
  408.     if (ent->item->giTag == PW_REDFLAG)
  409.         Team_ReturnFlagSound(Team_ResetFlag(TEAM_RED), TEAM_RED);
  410.     else if (ent->item->giTag == PW_BLUEFLAG)
  411.         Team_ReturnFlagSound(Team_ResetFlag(TEAM_BLUE), TEAM_BLUE);
  412.     // Reset Flag will delete this entity
  413. }
  414.  
  415. int Team_TouchOurFlag( gentity_t *ent, gentity_t *other, int team ) {
  416.     int i;
  417.     gentity_t *player;
  418.     gclient_t *cl = other->client;
  419.     int our_flag, enemy_flag;
  420.     gentity_t    *te;
  421.  
  422.     if (cl->sess.sessionTeam == TEAM_RED) {
  423.         our_flag = PW_REDFLAG;
  424.         enemy_flag = PW_BLUEFLAG;
  425.     } else {
  426.         our_flag = PW_BLUEFLAG;
  427.         enemy_flag = PW_REDFLAG;
  428.     }
  429.  
  430.     if ( ent->flags & FL_DROPPED_ITEM ) {
  431.         // hey, its not home.  return it by teleporting it back
  432.         PrintMsg( NULL, "%s" S_COLOR_WHITE " returned the %s flag!\n", 
  433.             cl->pers.netname, TeamName(team));
  434.         AddScore(other, CTF_RECOVERY_BONUS);
  435.         other->client->pers.teamState.flagrecovery++;
  436.         other->client->pers.teamState.lastreturnedflag = level.time;
  437.         //ResetFlag will remove this entity!  We must return zero
  438.         Team_ReturnFlagSound(Team_ResetFlag(team), team);
  439.         return 0;
  440.     }
  441.  
  442.     // the flag is at home base.  if the player has the enemy
  443.     // flag, he's just won!
  444.     if (!cl->ps.powerups[enemy_flag])
  445.         return 0; // We don't have the flag
  446.  
  447.     PrintMsg( NULL, "%s" S_COLOR_WHITE " captured the %s flag!\n",
  448.         cl->pers.netname, TeamName(OtherTeam(team)));
  449.  
  450.     cl->ps.powerups[enemy_flag] = 0;
  451.  
  452.     teamgame.last_flag_capture = level.time;
  453.     teamgame.last_capture_team = team;
  454.  
  455.     // Increase the team's score
  456.     level.teamScores[ other->client->sess.sessionTeam ]++;
  457.  
  458.     other->client->pers.teamState.captures++;
  459.  
  460.     // other gets another 10 frag bonus
  461.     AddScore(other, CTF_CAPTURE_BONUS);
  462.  
  463.     te = G_TempEntity( ent->s.pos.trBase, EV_GLOBAL_SOUND );
  464.     te->s.eventParm = G_SoundIndex( our_flag == PW_REDFLAG ?
  465.         "sound/teamplay/flagcap_red.wav" :
  466.         "sound/teamplay/flagcap_blu.wav" );
  467.     te->r.svFlags |= SVF_BROADCAST;
  468.  
  469.     // Ok, let's do the player loop, hand out the bonuses
  470.     for (i = 0; i < g_maxclients.integer; i++) {
  471.         player = &g_entities[i];
  472.         if (!player->inuse)
  473.             continue;
  474.  
  475.         if (player->client->sess.sessionTeam !=
  476.             cl->sess.sessionTeam) {
  477.             player->client->pers.teamState.lasthurtcarrier = -5;
  478.         } else if (player->client->sess.sessionTeam ==
  479.             cl->sess.sessionTeam) {
  480.             if (player != other)
  481.                 AddScore(player, CTF_TEAM_BONUS);
  482.             // award extra points for capture assists
  483.             if (player->client->pers.teamState.lastreturnedflag + 
  484.                 CTF_RETURN_FLAG_ASSIST_TIMEOUT > level.time) {
  485.                 PrintMsg( NULL, 
  486.                         "%s" S_COLOR_WHITE " gets an assist for returning the %s flag!\n", 
  487.                         player->client->pers.netname,
  488.                         TeamName(team));
  489.                 AddScore (player, CTF_RETURN_FLAG_ASSIST_BONUS);
  490.                 other->client->pers.teamState.assists++;
  491.             }
  492.             if (player->client->pers.teamState.lastfraggedcarrier + 
  493.                 CTF_FRAG_CARRIER_ASSIST_TIMEOUT > level.time) {
  494.                 PrintMsg( NULL, "%s" S_COLOR_WHITE " gets an assist for fragging the %s flag carrier!\n", 
  495.                         player->client->pers.netname,
  496.                         TeamName(OtherTeam(team)));
  497.                 AddScore(player, CTF_FRAG_CARRIER_ASSIST_BONUS);
  498.                 other->client->pers.teamState.assists++;
  499.             }
  500.         }
  501.     }
  502.     Team_ResetFlags();
  503.  
  504.     CalculateRanks();
  505.  
  506.     return 0; // Do not respawn this automatically
  507. }
  508.  
  509. int Team_TouchEnemyFlag( gentity_t *ent, gentity_t *other, int team ) {
  510.     gclient_t *cl = other->client;
  511.  
  512.     // hey, its not our flag, pick it up
  513.     PrintMsg (NULL, "%s" S_COLOR_WHITE " got the %s flag!\n",
  514.         other->client->pers.netname, TeamName(team));
  515.     AddScore(other, CTF_FLAG_BONUS);
  516.  
  517.     if (team == TEAM_RED)
  518.         cl->ps.powerups[PW_REDFLAG] = INT_MAX; // flags never expire
  519.     else
  520.         cl->ps.powerups[PW_BLUEFLAG] = INT_MAX; // flags never expire
  521.  
  522.     cl->pers.teamState.flagsince = level.time;
  523.  
  524.     Team_SetFlagStatus( team, FLAG_TAKEN );
  525.     
  526.     return -1; // Do not respawn this automatically, but do delete it if it was FL_DROPPED
  527. }
  528.  
  529. int Pickup_Team( gentity_t *ent, gentity_t *other ) {
  530.     int team;
  531.     gclient_t *cl = other->client;
  532.     
  533.     // figure out what team this flag is
  534.     if (strcmp(ent->classname, "team_CTF_redflag") == 0)
  535.         team = TEAM_RED;
  536.     else if (strcmp(ent->classname, "team_CTF_blueflag") == 0)
  537.         team = TEAM_BLUE;
  538.     else {
  539.         PrintMsg ( other, "Don't know what team the flag is on.\n");
  540.         return 0;
  541.     }
  542.  
  543.     return ((team == cl->sess.sessionTeam) ?
  544.         Team_TouchOurFlag : Team_TouchEnemyFlag)
  545.             (ent, other, team);
  546. }
  547.  
  548. /*
  549. ===========
  550. Team_GetLocation
  551.  
  552. Report a location for the player. Uses placed nearby target_location entities
  553. ============
  554. */
  555. gentity_t *Team_GetLocation(gentity_t *ent)
  556. {
  557.     gentity_t        *eloc, *best;
  558.     float            bestlen, len;
  559.     vec3_t            origin;
  560.  
  561.     best = NULL;
  562.     bestlen = 3*8192.0*8192.0;
  563.  
  564.     VectorCopy( ent->r.currentOrigin, origin );
  565.  
  566.     for (eloc = level.locationHead; eloc; eloc = eloc->nextTrain) {
  567.         len = ( origin[0] - eloc->r.currentOrigin[0] ) * ( origin[0] - eloc->r.currentOrigin[0] )
  568.             + ( origin[1] - eloc->r.currentOrigin[1] ) * ( origin[1] - eloc->r.currentOrigin[1] )
  569.             + ( origin[2] - eloc->r.currentOrigin[2] ) * ( origin[2] - eloc->r.currentOrigin[2] );
  570.  
  571.         if ( len > bestlen ) {
  572.             continue;
  573.         }
  574.  
  575.         if ( !trap_InPVS( origin, eloc->r.currentOrigin ) ) {
  576.             continue;
  577.         }
  578.  
  579.         bestlen = len;
  580.         best = eloc;
  581.     }
  582.  
  583.     return best;
  584. }
  585.  
  586.  
  587. /*
  588. ===========
  589. Team_GetLocation
  590.  
  591. Report a location for the player. Uses placed nearby target_location entities
  592. ============
  593. */
  594. qboolean Team_GetLocationMsg(gentity_t *ent, char *loc, int loclen)
  595. {
  596.     gentity_t *best;
  597.  
  598.     best = Team_GetLocation( ent );
  599.     
  600.     if (!best)
  601.         return qfalse;
  602.  
  603.     if (best->count) {
  604.         if (best->count < 0)
  605.             best->count = 0;
  606.         if (best->count > 7)
  607.             best->count = 7;
  608.         Com_sprintf(loc, loclen, "%c%c%s" S_COLOR_WHITE, Q_COLOR_ESCAPE, best->count + '0', best->message );
  609.     } else
  610.         Com_sprintf(loc, loclen, "%s", best->message);
  611.  
  612.     return qtrue;
  613. }
  614.  
  615.  
  616. /*---------------------------------------------------------------------------*/
  617.  
  618. /*
  619. ================
  620. SelectRandomDeathmatchSpawnPoint
  621.  
  622. go to a random point that doesn't telefrag
  623. ================
  624. */
  625. #define    MAX_TEAM_SPAWN_POINTS    16
  626. gentity_t *SelectRandomTeamSpawnPoint( int teamstate, team_t team ) {
  627.     gentity_t    *spot;
  628.     int            count;
  629.     int            selection;
  630.     gentity_t    *spots[MAX_TEAM_SPAWN_POINTS];
  631.     char        *classname;
  632.  
  633.     if (teamstate == TEAM_BEGIN) {
  634.         if (team == TEAM_RED)
  635.             classname = "team_CTF_redplayer";
  636.         else if (team == TEAM_BLUE)
  637.             classname = "team_CTF_blueplayer";
  638.         else
  639.             return NULL;
  640.     } else {
  641.         if (team == TEAM_RED)
  642.             classname = "team_CTF_redspawn";
  643.         else if (team == TEAM_BLUE)
  644.             classname = "team_CTF_bluespawn";
  645.         else
  646.             return NULL;
  647.     }
  648.     count = 0;
  649.  
  650.     spot = NULL;
  651.  
  652.     while ((spot = G_Find (spot, FOFS(classname), classname)) != NULL) {
  653.         if ( SpotWouldTelefrag( spot ) ) {
  654.             continue;
  655.         }
  656.         spots[ count ] = spot;
  657.         if (++count == MAX_TEAM_SPAWN_POINTS)
  658.             break;
  659.     }
  660.  
  661.     if ( !count ) {    // no spots that won't telefrag
  662.         return G_Find( NULL, FOFS(classname), classname);
  663.     }
  664.  
  665.     selection = rand() % count;
  666.     return spots[ selection ];
  667. }
  668.  
  669.  
  670. /*
  671. ===========
  672. SelectCTFSpawnPoint
  673.  
  674. ============
  675. */
  676. gentity_t *SelectCTFSpawnPoint ( team_t team, int teamstate, vec3_t origin, vec3_t angles ) {
  677.     gentity_t    *spot;
  678.  
  679.     spot = SelectRandomTeamSpawnPoint ( teamstate, team );
  680.  
  681.     if (!spot) {
  682.         return SelectSpawnPoint( vec3_origin, origin, angles );
  683.     }
  684.  
  685.     VectorCopy (spot->s.origin, origin);
  686.     origin[2] += 9;
  687.     VectorCopy (spot->s.angles, angles);
  688.  
  689.     return spot;
  690. }
  691.  
  692. /*---------------------------------------------------------------------------*/
  693.  
  694. static int QDECL SortClients( const void *a, const void *b ) {
  695.     return *(int *)a - *(int *)b;
  696. }
  697.  
  698.  
  699. /*
  700. ==================
  701. TeamplayLocationsMessage
  702.  
  703. Format:
  704.     clientNum location health armor weapon powerups
  705.  
  706. ==================
  707. */
  708. void TeamplayInfoMessage( gentity_t *ent ) {
  709.     char        entry[1024];
  710.     char        string[1400];
  711.     int            stringlength;
  712.     int            i, j;
  713.     gentity_t    *player;
  714.     int            cnt;
  715.     int            h, a;
  716.     int            clients[TEAM_MAXOVERLAY];
  717.  
  718.     if ( ! ent->client->pers.teamInfo )
  719.         return;
  720.  
  721.     // figure out what client should be on the display
  722.     // we are limited to 8, but we want to use the top eight players
  723.     // but in client order (so they don't keep changing position on the overlay)
  724.     for (i = 0, cnt = 0; i < g_maxclients.integer && cnt < TEAM_MAXOVERLAY; i++) {
  725.         player = g_entities + level.sortedClients[i];
  726.         if (player->inuse && player->client->sess.sessionTeam == 
  727.             ent->client->sess.sessionTeam ) {
  728.             clients[cnt++] = level.sortedClients[i];
  729.         }
  730.     }
  731.  
  732.     // We have the top eight players, sort them by clientNum
  733.     qsort( clients, cnt, sizeof( clients[0] ), SortClients );
  734.  
  735.     // send the latest information on all clients
  736.     string[0] = 0;
  737.     stringlength = 0;
  738.  
  739.     for (i = 0, cnt = 0; i < g_maxclients.integer && cnt < TEAM_MAXOVERLAY; i++) {
  740.         player = g_entities + i;
  741.         if (player->inuse && player->client->sess.sessionTeam == 
  742.             ent->client->sess.sessionTeam ) {
  743.  
  744.             h = player->client->ps.stats[STAT_HEALTH];
  745.             a = player->client->ps.stats[STAT_ARMOR];
  746.             if (h < 0) h = 0;
  747.             if (a < 0) a = 0;
  748.  
  749.             Com_sprintf (entry, sizeof(entry),
  750.                 " %i %i %i %i %i %i", 
  751. //                level.sortedClients[i], player->client->pers.teamState.location, h, a, 
  752.                 i, player->client->pers.teamState.location, h, a, 
  753.                 player->client->ps.weapon, player->s.powerups);
  754.             j = strlen(entry);
  755.             if (stringlength + j > sizeof(string))
  756.                 break;
  757.             strcpy (string + stringlength, entry);
  758.             stringlength += j;
  759.             cnt++;
  760.         }
  761.     }
  762.  
  763.     trap_SendServerCommand( ent-g_entities, va("tinfo %i%s", cnt, string) );
  764. }
  765.  
  766. void CheckTeamStatus(void)
  767. {
  768.     int i;
  769.     gentity_t *loc, *ent;
  770.  
  771.     if (level.time - level.lastTeamLocationTime > TEAM_LOCATION_UPDATE_TIME) {
  772.  
  773.         level.lastTeamLocationTime = level.time;
  774.  
  775.         for (i = 0; i < g_maxclients.integer; i++) {
  776.             ent = g_entities + i;
  777.             if (ent->inuse && 
  778.                 (ent->client->sess.sessionTeam == TEAM_RED ||
  779.                 ent->client->sess.sessionTeam == TEAM_BLUE)) {
  780.                 loc = Team_GetLocation( ent );
  781.                 if (loc)
  782.                     ent->client->pers.teamState.location = loc->health;
  783.                 else
  784.                     ent->client->pers.teamState.location = 0;
  785.             }
  786.         }
  787.  
  788.         for (i = 0; i < g_maxclients.integer; i++) {
  789.             ent = g_entities + i;
  790.             if (ent->inuse && 
  791.                 (ent->client->sess.sessionTeam == TEAM_RED ||
  792.                 ent->client->sess.sessionTeam == TEAM_BLUE)) {
  793.                 TeamplayInfoMessage( ent );
  794.             }
  795.         }
  796.     }
  797. }
  798.  
  799. /*-----------------------------------------------------------------*/
  800.  
  801. /*QUAKED team_CTF_redplayer (1 0 0) (-16 -16 -16) (16 16 32)
  802. Only in CTF games.  Red players spawn here at game start.
  803. */
  804. void SP_team_CTF_redplayer( gentity_t *ent ) {
  805. }
  806.  
  807.  
  808. /*QUAKED team_CTF_blueplayer (0 0 1) (-16 -16 -16) (16 16 32)
  809. Only in CTF games.  Blue players spawn here at game start.
  810. */
  811. void SP_team_CTF_blueplayer( gentity_t *ent ) {
  812. }
  813.  
  814.  
  815. /*QUAKED team_CTF_redspawn (1 0 0) (-16 -16 -24) (16 16 32)
  816. potential spawning position for red team in CTF games.
  817. Targets will be fired when someone spawns in on them.
  818. */
  819. void SP_team_CTF_redspawn(gentity_t *ent) {
  820. }
  821.  
  822. /*QUAKED team_CTF_bluespawn (0 0 1) (-16 -16 -24) (16 16 32)
  823. potential spawning position for blue team in CTF games.
  824. Targets will be fired when someone spawns in on them.
  825. */
  826. void SP_team_CTF_bluespawn(gentity_t *ent) {
  827. }
  828.  
  829.  
  830.  
  831.